package com.zrf.gulimall.search.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zrf.gulimall.commnon.core.es.SkuEsModel;
import com.zrf.gulimall.search.conf.ElasticConfig;
import com.zrf.gulimall.search.contanst.ElasticConstant;
import com.zrf.gulimall.search.service.MallSearchService;
import com.zrf.gulimall.search.vo.SearchParam;
import com.zrf.gulimall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author zhanrf
 * @Date 2021/12/25 13:48
 */
@Service
public class MallSearchServiceImpl implements MallSearchService {
	@Resource
	private RestHighLevelClient esClient;

	@Override
	public SearchResult search(SearchParam searchParam) throws IOException {
		SearchRequest searchRequest = buildSearchRequest(searchParam);
		SearchResponse search = esClient.search(searchRequest, ElasticConfig.COMMON_OPTIONS);
		return buildSearchResult(search, searchParam);
	}

	/**
	 * 构建检索结果
	 * @param searchResponse 检索响应对象
	 * @param searchParam 检索请求参数
	 * @return 检索结果
	 */
	private SearchResult buildSearchResult(SearchResponse searchResponse, SearchParam searchParam) {
		SearchResult searchResult = new SearchResult();
		SearchHit[] hits = searchResponse.getHits().getHits();
		if (ArrayUtil.isNotEmpty(hits)) {
			List<SkuEsModel> products = CollUtil.newArrayList();
			for (SearchHit hit : hits) {
				String sourceAsString = hit.getSourceAsString();
				SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
				if (StrUtil.isNotBlank(searchParam.getKeyword())) {
					HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
					skuEsModel.setSkuTitle(skuTitle.getFragments()[0].toString());
				}
				products.add(skuEsModel);
			}
			searchResult.setProduct(products);
		}
		if (CollUtil.isNotEmpty(searchResponse.getAggregations())) {
			// 分类聚合
			ParsedTerms catalogAgg = searchResponse.getAggregations().get("catalog_agg");
			List<SearchResult.CatalogVo> catalogVos = CollUtil.newArrayList();
			List<? extends Terms.Bucket> catalogAggBuckets = catalogAgg.getBuckets();
			for (Terms.Bucket bucket : catalogAggBuckets) {
				SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
				catalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());
				// 分类名字子聚合
				ParsedTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
				List<? extends Terms.Bucket> subBuckets = catalogNameAgg.getBuckets();
				if (CollUtil.isNotEmpty(subBuckets)) {
					catalogVo.setCatalogName(subBuckets.get(0).getKeyAsString());
				}
				catalogVos.add(catalogVo);
			}
			searchResult.setCatalogs(catalogVos);
			// 品牌聚合
			ParsedTerms brandAgg = searchResponse.getAggregations().get("brand_agg");
			List<SearchResult.BrandVo> brandVos = CollUtil.newArrayList();
			List<? extends Terms.Bucket> brandAggBuckets = brandAgg.getBuckets();
			for (Terms.Bucket bucket : brandAggBuckets) {
				SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
				brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
				// 品牌名称子聚合
				ParsedTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
				List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
				if (CollUtil.isNotEmpty(brandNameAggBuckets)) {
					brandVo.setBrandName(brandNameAggBuckets.get(0).getKeyAsString());
				}
				// 品牌图片子聚合
				ParsedTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
				List<? extends Terms.Bucket> brandImgAggBuckets = brandImgAgg.getBuckets();
				if (CollUtil.isNotEmpty(brandImgAggBuckets)) {
					brandVo.setBrandImg(brandImgAggBuckets.get(0).getKeyAsString());
				}
				brandVos.add(brandVo);
			}
			searchResult.setBrands(brandVos);
			// 嵌入式聚合
			ParsedNested nestedAgg = searchResponse.getAggregations().get("attr_agg");
			// 属性聚合
			ParsedTerms attrIdAgg = nestedAgg.getAggregations().get("attr_id_agg");
			List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
			List<SearchResult.AttrVo> attrVos = CollUtil.newArrayList();
			for (Terms.Bucket bucket : attrIdAggBuckets) {
				SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
				attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
				// 属性名称子聚合
				ParsedTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
				List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
				if (CollUtil.isNotEmpty(attrNameAggBuckets)) {
					attrVo.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
				}
				// 属性值
				ParsedTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
				List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
				if (CollUtil.isNotEmpty(attrValueAggBuckets)) {
					List<String> attrValues = attrValueAggBuckets.stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
					attrVo.setAttrValue(attrValues);
				}
				attrVos.add(attrVo);
			}
			searchResult.setAttrs(attrVos);
		}
		// 总记录数
		searchResult.setTotal(searchResponse.getHits().getTotalHits().value);
		// 当前页
		searchResult.setPageNum(searchParam.getPageNum());
		// 总页数
		long totalPages = (searchResult.getTotal() + ElasticConstant.PRODUCT_PAGE_SIZE - 1) / ElasticConstant.PRODUCT_PAGE_SIZE;
		searchResult.setTotalPages((int) totalPages);

		if (CollUtil.isNotEmpty(searchParam.getAttrs())) {
			Map<Long, SearchResult.AttrVo> attrMap = searchResult.getAttrs().stream().collect(Collectors.toMap(SearchResult.AttrVo::getAttrId, Function.identity()));
			List<SearchResult.NavVo> navVos = searchParam.getAttrs().stream().map(item -> {
				SearchResult.NavVo navVo = new SearchResult.NavVo();
				String[] s = item.split("_");
				navVo.setNavValue(s[1]);
				SearchResult.AttrVo attrVo = attrMap.get(Long.parseLong(s[0]));
				navVo.setNavName(attrVo.getAttrName());
				//2、取消了这个面包屑以后，我们要跳转到哪个地方，将请求的地址url里面的当前置空
				//拿到所有的查询条件，去掉当前
				String encode = null;
				try {
					encode = URLEncoder.encode(item,"UTF-8");
					encode.replace("+","%20");  //浏览器对空格的编码和Java不一样，差异化处理
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				String replace = searchParam.get_queryString().replace("&attrs=" + item, "");
				navVo.setLink("http://search.gulimall.com/list.html?" + replace);
				return navVo;
			}).collect(Collectors.toList());
			searchResult.setNavs(navVos);
		}
		return searchResult;
	}

	/**
	 * 构建检索请求
	 * @param searchParam 检索条件
	 * @return 检索请求
	 */
	private SearchRequest buildSearchRequest(SearchParam searchParam) {
		// 创建request
		SearchRequest searchRequest = new SearchRequest(ElasticConstant.PRODUCT_INDEX);
		// 创建查询条件
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
		// 商品标题
		if (StrUtil.isNotBlank(searchParam.getKeyword())) {
			boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
			// 高亮
			searchSourceBuilder.highlighter(new HighlightBuilder().field("skuTitle").preTags("<b style='color:red'>").preTags("</b>"));
		}
		// 分类id
		if (Objects.nonNull(searchParam.getCatalog3Id())) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
		}
		// 品牌id
		if (CollUtil.isNotEmpty(searchParam.getBrandId())) {
			boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
		}
		// 是否有库存
		if (Objects.nonNull(searchParam.getHasStock())) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
		}
		// 属性
		if (CollUtil.isNotEmpty(searchParam.getAttrs())) {
			List<String> attrs = searchParam.getAttrs();
			for (String attr : attrs) {
				BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
				String[] split = attr.split("_");
				nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
				String[] attrValues = split[1].split(":");
				nestedBoolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
				boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", nestedBoolQueryBuilder, ScoreMode.None));
			}
		}
		// 价格区间
		if (StrUtil.isNotBlank(searchParam.getSkuPrice())) {
			// "_xx" 会分割成两个，""和"xx"
			String[] price = searchParam.getSkuPrice().split("_");
			if (price.length > 1) {
				boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(StrUtil.isBlank(price[0]) ? 0 : price[0]).lte(price[1]));
			} else {
				boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(price[0]));
			}
		}
		searchSourceBuilder.query(boolQueryBuilder);
		// 排序
		if (StrUtil.isNotBlank(searchParam.getSort())) {
			String[] sort = searchParam.getSort().split("_");
			searchSourceBuilder.sort(sort[0], "asc".equalsIgnoreCase(sort[1]) ? SortOrder.ASC : SortOrder.DESC);
		}
		// 分页
		searchSourceBuilder.from(ElasticConstant.PRODUCT_PAGE_SIZE * (searchParam.getPageNum() - 1));
		searchSourceBuilder.size(ElasticConstant.PRODUCT_PAGE_SIZE);
		// 品牌聚合
		searchSourceBuilder.aggregation(
				AggregationBuilders.terms("brand_agg").field("brandId").size(50)
						.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1))
						.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1)));
		// 分类聚合
		searchSourceBuilder.aggregation(
				AggregationBuilders.terms("catalog_agg").field("catalogId").size(20)
						.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1)));
		// 属性聚合
		searchSourceBuilder.aggregation(
				AggregationBuilders.nested("attr_agg", "attrs")
						.subAggregation(
								AggregationBuilders.terms("attr_id_agg").field("attrs.attrId")
										.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1))
										.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50))));
		searchRequest.source(searchSourceBuilder);
		return searchRequest;
	}
}
